package com.sinoparasoft.caldeirao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.openstack4j.api.Builders;
import org.openstack4j.api.OSClient;
import org.openstack4j.api.types.Facing;
import org.openstack4j.model.compute.ActionResponse;
import org.openstack4j.model.compute.FloatingIP;
import org.openstack4j.model.compute.Server;
import org.openstack4j.model.identity.Role;
import org.openstack4j.model.identity.Tenant;
import org.openstack4j.model.identity.User;
import org.openstack4j.model.network.AttachInterfaceType;
import org.openstack4j.model.network.IP;
import org.openstack4j.model.network.IPVersionType;
import org.openstack4j.model.network.NetFloatingIP;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.NetworkType;
import org.openstack4j.model.network.Pool;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.Router;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.model.network.options.PortListOptions;
import org.openstack4j.model.telemetry.Alarm;
import org.openstack4j.model.telemetry.Sample;
import org.openstack4j.model.telemetry.SampleCriteria;
import org.openstack4j.model.telemetry.Alarm.Type;
import org.openstack4j.model.telemetry.Alarm.ThresholdRule.ComparisonOperator;
import org.openstack4j.model.telemetry.Alarm.ThresholdRule.Statistic;
import org.openstack4j.openstack.OSFactory;
import org.openstack4j.openstack.telemetry.domain.CeilometerAlarm;
import org.openstack4j.openstack.telemetry.domain.CeilometerAlarm.CeilometerQuery;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestMethod;
//import org.springframework.web.bind.annotation.ResponseBody;

public class Cloud {
	private final String OS_AUTH_URL = "http://192.168.100.131:5000/v2.0/";
	private final String OS_USERNAME = "admin";
	private final String OS_PASSWORD = "e2c255ae89564c9a";
	String publicNetworkId = "0bf4abe2-400d-4932-9089-502757d8a319";
	
	String dnsHost = "124.16.136.254";
	
	
//	private final String OS_AUTH_URL = "http://192.168.100.211:5000/v2.0/";
//	private final String OS_USERNAME = "admin";
//	private final String OS_PASSWORD = "2c0c5efa5ca74e0e";
//	String publicNetworkId = "061331c4-3669-4f94-8c29-482270ab7516";
	
	public Cloud() {
	}
	
	public List<? extends NetFloatingIP> getFloatingIPs() {
		OSClient adminTenantClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.authenticate();
		
		List<? extends NetFloatingIP> floatingIPs = adminTenantClient.networking().floatingip().list();
		return floatingIPs; 
	}

	public List<? extends Port> getPublicPorts() {
		OSClient adminTenantClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.authenticate();
		
		List<? extends Port> ports = adminTenantClient.networking().port().list(PortListOptions.create().networkId(publicNetworkId));
		return ports;
	}
	
	public List<ExternalIp> getExternalIps() {
		OSClient adminTenantClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.authenticate();
				
		List<ExternalIp> ips = new ArrayList<ExternalIp>();
		Map<String, String> ipMap = mapFloatingIpToVirtualMachine();
		List<? extends Port> gateways = adminTenantClient.networking().port().list(PortListOptions.create().networkId(publicNetworkId).deviceOwner("network:router_gateway"));
		
		// TODO assert getSubnets().get(0) == 1
		String subnetId = adminTenantClient.networking().network().get(publicNetworkId).getSubnets().get(0);		
		Subnet subnet =  adminTenantClient.networking().subnet().get(subnetId);
		String cidr = subnet.getCidr();
		int prefix = Integer.parseInt(cidr.substring(cidr.indexOf('/') + 1));
		// TODO assert prefix == 24
		List<? extends Pool> pools = subnet.getAllocationPools();
		// TODO assert pools.size() == 1
		Pool p = pools.get(0);

		String ipSegment = p.getStart().substring(0, p.getStart().lastIndexOf('.'));
		int start = Integer.parseInt(p.getStart().substring(p.getStart().lastIndexOf('.') + 1));
		int end = Integer.parseInt(p.getEnd().substring(p.getEnd().lastIndexOf('.') + 1));		
		
		for (int i = start; i <= end; i++) {
			ExternalIp ip = new ExternalIp();
			String address = ipSegment + "." + i;
			ip.setAddress(address);
			
			// 找到浮动IP后加入列表，然后重新处理下一个
			if (ipMap.containsKey(address)) {
				ip.setStatus("floating ip of " + ipMap.get(address));
				ips.add(ip);
				
				continue;
			}
			
			for (Port gateway: gateways) {
				// TODO assert gateway.getFixedIps().size() == 1
				IP fixedIp = (IP)gateway.getFixedIps().toArray()[0];
				if (fixedIp.getIpAddress().equalsIgnoreCase(address)) {
					ip.setStatus("gateway");
					
					break;
				}
			}

			ips.add(ip);		
		}		
				
		return ips;
	}
	
	private Map<String, String> mapFloatingIpToVirtualMachine() {
		Map<String, String> map = new HashMap<String, String>();

		OSClient adminTenantClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.authenticate();
		
		List<? extends Tenant> tenants = adminTenantClient.identity().tenants().list();
		for (Tenant tenant: tenants) {
			OSClient tenantClient = OSFactory.builder()
					.endpoint(OS_AUTH_URL)
					.credentials(OS_USERNAME, OS_PASSWORD)
					.tenantName(tenant.getName())
					.authenticate();

			List<? extends FloatingIP> floatingIps = tenantClient.compute().floatingIps().list();			
			for (FloatingIP floatingIp: floatingIps) {
				map.put(floatingIp.getFloatingIpAddress(), floatingIp.getInstanceId());
			}
		}
		
		return map;
	}
	
	public Tenant createTenant(String tenantName, String tenantDiscription) {
		OSClient adminClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.perspective(Facing.ADMIN)
				.authenticate();
		
		Tenant t = Builders.tenant().name(tenantName).description(tenantDiscription).build();
		Tenant tenant = adminClient.identity().tenants().create(t);
		
//		Tenant tenant = adminClient.identity().tenants().getByName("tenant1");
//		List<? extends User> users = adminClient.identity().users().listTenantUsers(tenant.getId());
		User admin = adminClient.identity().users().getByName("admin");
//		List<? extends Role> roles = adminClient.identity().users().listRolesOnTenant(admin.getId(), tenant.getId());
		Role adminRole = adminClient.identity().roles().getByName("admin");	
		ActionResponse response = adminClient.identity().roles().addUserRole(tenant.getId(), admin.getId(), adminRole.getId());	// Entity must not be null for http method PUT.
		System.out.println(response.toString());
		
		Network tenantNetwork = adminClient.networking().network().create(Builders.network()
				.name(tenantName + "_network")
				.networkType(NetworkType.VXLAN)
				.tenantId(tenant.getId())
				.adminStateUp(true)		////////
				.build());
		Subnet tenantSubnet = adminClient.networking().subnet().create(Builders.subnet()
				.addDNSNameServer(dnsHost)
				.cidr("172.16.1.0/24")
				.gateway("172.16.1.1")
				.ipVersion(IPVersionType.V4)	////////
				.name(tenantName + "_subnet")
				.network(tenantNetwork)
				.tenantId(tenant.getId())
				.enableDHCP(true)		////////
				.build());
		
		Network publicNetwork = adminClient.networking().network().get(publicNetworkId);
		Router tenantRouter = adminClient.networking().router().create(Builders.router()
				.externalGateway(publicNetwork.getId())
				.name(tenantName + "_router")
				.tenantId(tenant.getId())
				.adminStateUp(true)		////////
				.build());
		adminClient.networking().router().attachInterface(tenantRouter.getId(), AttachInterfaceType.SUBNET, tenantSubnet.getId());
		
		return tenant;
	}
	
	public List<? extends Tenant> getTenants() {
		OSClient adminClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("admin")
				.perspective(Facing.ADMIN)
				.authenticate();
		
		return adminClient.identity().tenants().list();
	}
	
	//
	
	
	private static void showMetric(String instanceId) {
		OSClient os = OSFactory.builder()
				.endpoint("http://192.168.211.147:5000/v2.0/")
				.credentials("admin", "93363cc6031f4bfa").tenantName("admin")
				.tenantId("a56b6247000c4fc0861eec38bc382434").authenticate();

		SampleCriteria criteria = new SampleCriteria();
		criteria = criteria.resource(instanceId).timestamp(SampleCriteria.Oper.GT, System.currentTimeMillis() - (300 * 60 * 1000));
		List<? extends Sample> samples = os.telemetry().meters().samples("disk.read.bytes.rate", criteria);
		List<String> time_series = new ArrayList<String>();
		List<Float> cpu_load = new ArrayList<Float>();		
		for (Sample s: samples) {
			System.out.println(s.getCounterVolume() + s.getCounterUnit() + "\t" + s.getRecordedAt());
			time_series.add(s.getRecordedAt());
			cpu_load.add(s.getCounterVolume());
		}
	}

	private static void createAlarm(String instanceId) {
		OSClient os = OSFactory.builder()
				.endpoint("http://192.168.211.147:5000/v2.0/")
				.credentials("admin", "93363cc6031f4bfa").tenantName("admin")
				.tenantId("a56b6247000c4fc0861eec38bc382434").authenticate();

		CeilometerAlarm.CeilometerThresholdRule thRule = new CeilometerAlarm.CeilometerThresholdRule();
		thRule.setMeterName("cpu_util");
		thRule.setThreshold(80.0f);
		thRule.setComparisonOperator(ComparisonOperator.GE);
		thRule.setStatistic(Statistic.AVG);
		thRule.setPeriod(60);
		thRule.setEvaluationPeriods(3);

		List<CeilometerQuery> qryList = new ArrayList<CeilometerAlarm.CeilometerQuery>();
		CeilometerQuery cmQuery = new CeilometerQuery();
		cmQuery.setField("resource_id");
		cmQuery.setOp(ComparisonOperator.EQ);
		// set instance id
		cmQuery.setValue(instanceId);
		qryList.add(cmQuery);
		thRule.setQuery(qryList);

		List<String> actList = new ArrayList<String>();
		actList.add("log://");

		Alarm alm = new CeilometerAlarm();
		// alarm name must be unique inside the tenant, better suffix with the instance id 
		alm.setName("cpu_load_alarm_" + instanceId);
		alm.setDescription("instance cpu usage high");
		alm.setType(Type.THRESHOLD);
		alm.setThresholdRule(thRule);
		alm.setAlarmActions(actList);
		alm.isEnabled(true);

		alm = os.telemetry().alarms().create(alm);
		System.out.println(alm.getAlarmId());

		return;
	}

	private static void getAlarm(String alarmId) {
		OSClient os = OSFactory.builder()
				.endpoint("http://192.168.211.147:5000/v2.0/")
				.credentials("admin", "93363cc6031f4bfa").tenantName("admin")
				.tenantId("a56b6247000c4fc0861eec38bc382434").authenticate();

		Alarm alm = os.telemetry().alarms().getById(alarmId);
		System.out.println(alm.getState());

		return;
	}
	
	
/*	@RequestMapping(value="/cpu/{id}", method=RequestMethod.GET)
	@ResponseBody
	public Object cpuChart(@PathVariable("id") String id) {
		OSClient os = OSFactory.builder()
				.endpoint("http://192.168.211.147:5000/v2.0/")
				.credentials("admin", "93363cc6031f4bfa").tenantName("admin").tenantId("a56b6247000c4fc0861eec38bc382434")
				.authenticate();	

		SampleCriteria criteria = new SampleCriteria();
		criteria = criteria.resource(id).timestamp(SampleCriteria.Oper.GT, System.currentTimeMillis() - (300 * 60 * 1000));
		List<? extends Sample> samples = os.telemetry().meters().samples("cpu_util", criteria);
		List<String> time_series = new ArrayList<String>();
		List<Float> cpu_load = new ArrayList<Float>();		
		for (Sample s: samples) {
			System.out.println(s.getCounterVolume() + s.getCounterUnit() + "\t" + s.getRecordedAt());
			time_series.add(s.getRecordedAt());
			cpu_load.add(s.getCounterVolume());
		}

		Map<Object, Object> map = new HashMap<Object, Object>();
		map.put("time_series", time_series);
		map.put("cpu_load", cpu_load);
		
		return map;
	}*/
	
	
	public void addFloatingIpToServer() {
		OSClient tenantClient = OSFactory.builder()
				.endpoint(OS_AUTH_URL)
				.credentials(OS_USERNAME, OS_PASSWORD)
				.tenantName("demo2")
				.authenticate();
		
		Server server = tenantClient.compute().servers().get("fef861ac-eb86-438d-b892-2dc1e6f977f8");
		tenantClient.compute().floatingIps().addFloatingIP(server, "192.168.100.128");
		// tenantClient.compute().floatingIps().addFloatingIP(server, "192.168.0.250", "50.50.2.3");
		
		return;
	}
}
